package com.neuronbit.businessflow.flow.clazz;

import com.neuronbit.businessflow.intellij.model.Type;
import com.neuronbit.javaparser.resolution.types.ResolvedLambdaConstraintType;
import com.neuronbit.javaparser.resolution.types.ResolvedType;
import org.apache.commons.lang3.StringUtils;

import java.util.stream.Collectors;

/**
 * The type Source type.
 */
public class SourceType implements Type {
    private final String qualifiedName;
    private final String name;
    private ResolvedType resolvedType;

    private SourceType(ResolvedType resolvedType) {
        this.resolvedType = resolvedType;
        this.qualifiedName = resolveQualifiedName();
        this.name = StringUtils.substringAfterLast(getQualifiedName(), ".");
    }

    /**
     * Of source type.
     *
     * @param resolvedType the resolved type
     * @return the source type
     */
    public static SourceType of(ResolvedType resolvedType) {
        return new SourceType(resolvedType);
    }

    @Override
    public String getQualifiedName() {
        return this.qualifiedName;
    }

    @Override
    public String getName() {
        return name;
    }

    private String resolveQualifiedName() {
        if (resolvedType.isArray()) {
            return resolvedType.asArrayType().describe();
        }
        if (resolvedType.isPrimitive()) {
            return resolvedType.asPrimitive().getBoxTypeQName();
        }
        if (resolvedType.isNull()) {
            return "null";
        }
        if (resolvedType.isUnionType()) {
            return resolvedType.asUnionType()
                    .getElements()
                    .stream()
                    .map(e -> SourceType.of(e).getQualifiedName())
                    .collect(Collectors.joining(","));
        }
        if (resolvedType.isReference()) {
            return resolvedType.asReferenceType().getQualifiedName();
        }
        if (resolvedType.isConstraint()) {
            ResolvedLambdaConstraintType constraintType = resolvedType.asConstraintType();
            return SourceType.of(constraintType.getBound()).getQualifiedName();
        }
        if (resolvedType.isReferenceType()) {
            return resolvedType.asReferenceType().getQualifiedName();
        }
        if (resolvedType.isVoid()) {
            return "void";
        }
        if (resolvedType.isTypeVariable()) {
            return resolvedType.asTypeVariable().qualifiedName();
        }
        if (resolvedType.isWildcard()) {
            return SourceType.of(resolvedType.asWildcard().getBoundedType()).getQualifiedName();
        }
        if (resolvedType.isInferenceVariable()) {
            throw new UnsupportedTypeException(resolvedType);
        }

        throw new UnsupportedTypeException(resolvedType);
    }
}
